#include <iostream>
#include <thread>
#include <vector>
#include <functional>
#include <mutex>
#include <future>
#include <condition_variable>

class ThreadPool
{
public:
    using func_t = std::function<void()>;
    ThreadPool(int thread_nums = 1) : _stop(false)
    {
        for (int i = 0; i < thread_nums; i++)
        {
            _threads.emplace_back(&ThreadPool::entry, this);
        }
    }

    ~ThreadPool()
    {
        Stop();
    }
    template <typename F, typename... Args>
    // -> : C++11新特性, 配合auto使用, 指定最终返回值类型
    // decltype是一个关键字(C++11), 可用来推导函数的返回类型, 这里的意思是根据传入的函数及参数进行推导, 推导出传入函数的返回值
    auto Push(const F &&func, Args &&...args) -> std::future<decltype(func(args...))>
    {
        // auto ptask = std::make_shared<std::packaged_task<int(int, int)>>(Add);
        // 进行类型推导
        using return_type = decltype(func(args...));
        // 将传入的函数和参数“保持”住，形成一个新的可调用对象
        // 后续线程进行执行时不需要传递任何参数即可直接执行
        auto tmp_func = std::bind(std::forward<F>(func), std::forward<Args>(args)...);
        auto ptask = std::make_shared<std::packaged_task<return_type()>>(tmp_func);
        std::future<return_type> fu = ptask->get_future();
        // 2. 构造一个lambda匿名函数（捕获任务对象），函数内执行任务对象
        {
            std::unique_lock<std::mutex> lock(_mtx);
            // 3. 将构造出来的匿名函数对象，抛入到任务池中
            // 注意这里不可以直接传(*ptask)(); 它会直接执行
            _tasks.push_back([ptask]()
                             { (*ptask)(); });
            _cv.notify_one();
        }
        return fu;
    }
    void Stop()
    {
        if (_stop == true)
            return;
        _stop = true;
        // 停止退出时唤醒所有的线程, 把任务执行完毕然后进行等待退出;
        _cv.notify_all();

        for (auto &thread : _threads)
        {
            thread.join();
        }
    }

private:
    void entry()
    {
        while (!_stop)
        {
            std::vector<func_t> tmp_tasks;

            {
                // 加锁
                std::unique_lock<std::mutex> lock(_mtx);
                // 条件变量设置阻塞条件(lambda表达式, 一般可调用对象返回的是bool类型)
                // 任务队列不为空 或者 _stop 被置位 继续运行
                // 为 false 就阻塞 等待被唤醒
                // 唤醒线程的方式: notify_all,  notify_one,  为此条件满足(true)
                // 已经停止服务线程就可以进行后续任务执行, 执行完毕后对线程进行回收
                _cv.wait(lock, [this]()
                         { return _stop || !_tasks.empty(); });
                tmp_tasks.swap(_tasks); // 为了避免频繁的申请锁(每次执行一个任务申请一次锁, 一次多取几个任务)
            }

            for (auto &task : tmp_tasks)
            {
                task();
            }
        }
    }

private:
    std::vector<func_t> _tasks;        // 任务列表
    std::mutex _mtx;                   // 锁
    std::condition_variable _cv;       // 条件变量, 用于同步任务列表的状态;
    std::atomic<bool> _stop;           // 线程池运行状态(原子的)
    std::vector<std::thread> _threads; // 线程池 细节点: 线程池要放在锁和条件变量之后;确保创建线程之前锁和条件变量以经被初始化;
};
int Add(int x, int y)
{
    return x + y;
}
int main()
{
    ThreadPool tp;
    for (int i = 0; i < 10; i++)
    {
        auto fu = tp.Push(Add, 11, i);
        int res = fu.get();
        std::cout << res << std::endl;
    }

    tp.Stop();

    return 0;
}